package GUI;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import manager.Manager;
import manager.ParameterSet;


/**
 * Cette classe est l'interface graphique de l'application.
 * Elle permet à l'utilisateur de spécifier les données de son problème,
 * de choisir une résolution par CPlex ou VNS (ou les deux), de choisir une sortie log,
 * une sortie verbose, etc.
 * Elle intègre une sortie texte pour l'utilisateur.
 * 
 * @author BERNARD Arthur, DELAMARCHE Jérôme, PAVAGEAU Max
 */
@SuppressWarnings("serial")
public class PNEGUI extends JFrame implements ActionListener {
	/**
	 * Les checkbox pour les options.
	 */
	private final JCheckBox verboseCB, logCB, primalCB, dualCB;
	/**
	 * Les deux boutons de chargement de fichier et de lancement du calcul.
	 */
	private final JButton chargerB, lancerB;
	/**
	 * Les 3 champs modifiables par l'utilisateur concernant les conditions d'arrêt.
	 */
	private final JTextField nbIterMaxText, nbVisitMaxText, epsilonText;
	/**
	 * Le panneau de sortie texte pour l'utilisateur.
	 */
	private final JTextArea textOut;
	/**
	 * Un pannel scrollable pour le pannel de sortie texte.
	 */
	private final JScrollPane textOutScroll;
	/**
	 * Un sélectionneur de fichier pour l'utilisateur.
	 */
	private final JFileChooser fc;
	
	/**
	 * Une chaîne constante pour représenter un saut de ligne.
	 */
	public static final String endl = "\n";
	
	/**
	 * Le Manager (programme principal) associé à cette interface.
	 */
	private final Manager manager;
	
	/**
	 * Le constructeur par défaut. Il construit et affiche toute l'interface utilisateur.
	 */
	public PNEGUI(Manager manager) {
		super("PNE - Résolution VNS");
		this.manager = manager;
		
		verboseCB = new JCheckBox("verbose");
		logCB = new JCheckBox("log");
		primalCB = new JCheckBox("VNS (borne primale)");
		dualCB = new JCheckBox("CPlex (borne duale)");
		chargerB = new JButton("Charger");
		lancerB = new JButton("Lancer");
		nbIterMaxText = new JTextField("10000");
		nbIterMaxText.setMargin(new Insets(0, 3, 0, 0));
		nbIterMaxText.setMaximumSize(new Dimension(50, 20));
		nbIterMaxText.setPreferredSize(new Dimension(50, 20));
		nbIterMaxText.setMinimumSize(new Dimension(50, 20));
		nbVisitMaxText = new JTextField("10");
		nbVisitMaxText.setMargin(new Insets(0, 3, 0, 0));
		nbVisitMaxText.setMaximumSize(new Dimension(50, 20));
		nbVisitMaxText.setPreferredSize(new Dimension(50, 20));
		nbVisitMaxText.setMinimumSize(new Dimension(50, 20));
		epsilonText = new JTextField("20");
		epsilonText.setMargin(new Insets(0, 3, 0, 0));
		epsilonText.setMaximumSize(new Dimension(50, 20));
		epsilonText.setPreferredSize(new Dimension(50, 20));
		epsilonText.setMinimumSize(new Dimension(50, 20));
		fc = new JFileChooser();
		
		JPanel tmpPanel, mainPanel;
		
		Container pane = this.getContentPane();
		mainPanel = new JPanel();
		mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
		pane.add(mainPanel);
		
		
		tmpPanel = new JPanel();
		tmpPanel.setBorder(BorderFactory.createTitledBorder("Paramètres"));
		tmpPanel.setLayout(new BoxLayout(tmpPanel, BoxLayout.X_AXIS));
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(new JLabel("nbIterMax: "));
		tmpPanel.add(nbIterMaxText);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(new JLabel("nbVisitMax: "));
		tmpPanel.add(nbVisitMaxText);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(new JLabel("epsilon: "));
		tmpPanel.add(epsilonText);
		tmpPanel.add(Box.createHorizontalGlue());
		mainPanel.add(tmpPanel);
		mainPanel.add(Box.createVerticalStrut(5));
		
		tmpPanel = new JPanel();
		tmpPanel.setBorder(BorderFactory.createTitledBorder("Options"));
		tmpPanel.setLayout(new BoxLayout(tmpPanel, BoxLayout.X_AXIS));
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(primalCB);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(dualCB);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(verboseCB);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(logCB);
		tmpPanel.add(Box.createHorizontalGlue());
		mainPanel.add(tmpPanel);
		mainPanel.add(Box.createVerticalStrut(10));;
		
		tmpPanel = new JPanel();
		tmpPanel.setLayout(new BoxLayout(tmpPanel, BoxLayout.X_AXIS));
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(chargerB);
		tmpPanel.add(Box.createHorizontalGlue());
		tmpPanel.add(lancerB);
		tmpPanel.add(Box.createHorizontalGlue());
		mainPanel.add(tmpPanel);
		mainPanel.add(Box.createVerticalStrut(5));
		
		tmpPanel = new JPanel();
		tmpPanel.setBorder(BorderFactory.createTitledBorder("Sortie"));
		mainPanel.add(tmpPanel);
		
		textOut = new JTextArea(21, 85);
		textOut.setFont(new Font("Courier New", Font.PLAIN, 14));
		textOut.setEditable(false);
		textOut.setMargin(new Insets(3, 4, 3, 4));
		textOut.setLineWrap(true);
		textOutScroll = new JScrollPane(textOut, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		tmpPanel.add(textOutScroll);
		
		verboseCB.addActionListener(this);
		logCB.addActionListener(this);
		primalCB.addActionListener(this);
		dualCB.addActionListener(this);
		lancerB.addActionListener(this);
		chargerB.addActionListener(this);
		
		this.setMinimumSize(new Dimension(600, 600));
		this.setResizable(false);
		this.pack();
		
		JFrame.setDefaultLookAndFeelDecorated(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}

	/**
	 * Permet de récupérer le champ correspondant au paramètre NbIterMax.
	 * 
	 * @return Le champ NbIterMax.
	 */
	public JTextField getNbIterMaxText() {
		return nbIterMaxText;
	}

	/**
	 * Permet de récupérer le champ correspondant au paramètre NbVisitMax.
	 * 
	 * @return Le champ NbVisitMax.
	 */
	public JTextField getNbVisitMaxText() {
		return nbVisitMaxText;
	}

	/**
	 * Permet de récupérer le champ correspondant au paramètre epsilon.
	 * 
	 * @return Le champ epsilon.
	 */
	public JTextField getEpsilonText() {
		return epsilonText;
	}
	
	/**
	 * Une méthode permettant une sortie texte sur le pannel d'affichage pour l'utilisateur.
	 * 
	 * @param str La chaîne à ajouter au pannel.
	 */
	public void printOut(String str) {
		textOut.append(str);
		textOutScroll.getVerticalScrollBar().setValue(textOutScroll.getVerticalScrollBar().getMaximum());
	}
	
	/**
	 * Une méthode permettant une sortie texte sur le pannel d'affichage pour l'utilisateur.
	 * Elle effectue un saut de ligne à la fin de la chaîne.
	 * 
	 * @param str La chaîne à ajouter au pannel.
	 */
	public void printOutln(String str) {
		textOut.append(str + endl);
		textOutScroll.getVerticalScrollBar().setValue(textOutScroll.getVerticalScrollBar().getMaximum());
	}
	
	/**
	 * Cette méthode permet de mettre à jour les éléments de la GUI en fonction
	 * du ParameterSet passé en paramètre.
	 * 
	 * @param param Le ParameterSet sevant à mettre à jour l'interface.
	 */
	public void notifyParamChanged(ParameterSet param) {
		nbIterMaxText.setText((int)param.getDouble("nbitermax") + "");
		nbVisitMaxText.setText((int)param.getDouble("nbvisitmax") + "");
		epsilonText.setText(param.getDouble("epsilon") + "");
		
		primalCB.setSelected(param.getBool("vns"));
		dualCB.setSelected(param.getBool("cplex"));
		logCB.setSelected(param.getBool("log"));
		verboseCB.setSelected(param.getBool("verbose"));
	}
	
	public void disableActions() {
		nbIterMaxText.setEnabled(false);
		nbVisitMaxText.setEnabled(false);
		epsilonText.setEnabled(false);
		verboseCB.setEnabled(false);
		logCB.setEnabled(false);
		primalCB.setEnabled(false);
		dualCB.setEnabled(false);
		lancerB.setEnabled(false);
		chargerB.setEnabled(false);
	}
	
	public void enableActions() {
		nbIterMaxText.setEnabled(true);
		nbVisitMaxText.setEnabled(true);
		epsilonText.setEnabled(true);
		verboseCB.setEnabled(true);
		logCB.setEnabled(true);
		primalCB.setEnabled(true);
		dualCB.setEnabled(true);
		lancerB.setEnabled(true);
		chargerB.setEnabled(true);
	}
	
	/**
	 * Gestion des évènements de l'interface.
	 * 
	 * @param arg0 L'évènement reçu.
	 */
	public void actionPerformed(ActionEvent arg0) {
		if(arg0.getSource() == chargerB) {
	        int returnVal = fc.showOpenDialog(this);

	        if (returnVal == JFileChooser.APPROVE_OPTION) {
	            File file = fc.getSelectedFile();
	            
	            this.printOutln("Ouverture: " + file.getName() + ".");
	            manager.readFromFile(file);
	        }
		}
		else if(arg0.getSource() == verboseCB) {
			if(verboseCB.isSelected())
				manager.getParam().setParam("verbose", true);
			else
				manager.getParam().setParam("verbose", false);
		}
		else if(arg0.getSource() == logCB) {
			if(logCB.isSelected())
				manager.getParam().setParam("log", true);
			else
				manager.getParam().setParam("log", false);
		}
		else if(arg0.getSource() == primalCB) {
			if(primalCB.isSelected())
				manager.getParam().setParam("vns", true);
			else
				manager.getParam().setParam("vns", false);
		}
		else if(arg0.getSource() == dualCB) {
			if(dualCB.isSelected())
				manager.getParam().setParam("cplex", true);
			else
				manager.getParam().setParam("cplex", false);
		}
		else if(arg0.getSource() == lancerB) {
			this.printOutln("Lancement !");

			Thread performProcess = new Thread(new Runnable() {
				public void run() {
					manager.performProcess();
				}
			});
			performProcess.start();
		}
	}
}
